package indexingSystem;

import java.util.Vector;

public class Clusters {
	private Vector<Cluster> clusters = null;
	private Vector<Document> documents = new Vector<Document>();
	private ClusterDictionary dictionary = null;
	private int maxClusterSize = -1;
	public void addDocument(Document doc) throws Exception {
		if (dictionary != null) {
			doc.calcTFV(dictionary);
		}
		documents.add(doc);
	}
	public void setDictionary(ClusterDictionary dict) throws Exception {
		dictionary = dict;
		if (dict == null)
			return;
		for (Document doc : documents)
			doc.calcTFV(dictionary);
	}
	public void setMaxClusterSize(int max) {
		maxClusterSize = max;
	}
	private void initialize() throws Exception {
		int num_docs = documents.size();
		if (num_docs < 2)
			throw new Exception("Not enough documents to cluster.");
		int num_clusters = 2;
		if (maxClusterSize != -1) {
			if ( num_docs/maxClusterSize > 0 )
				num_clusters = num_docs/maxClusterSize;
		}
		int vector_size = dictionary.getVector().size();
		clusters = new Vector<Cluster>(num_clusters);
		int clusterSize = num_docs / num_clusters;
		int rem = num_docs % num_clusters;
		Cluster c;
		for(int i = 0; i < num_clusters; i++) {
			c = new Cluster(vector_size);
			clusters.add(c);
			c.startUpdate();
			for(int j = 0; j < clusterSize; j++)
				clusters.get(i).addDocument(documents.get(i*clusterSize+j));
			c.endUpdateChanged();
		}
		if (rem > 0) {
			c = clusters.get(0);
			c.startUpdate();
			for(int i = 1; i <= rem; i++)
				c.addDocument(documents.get(num_docs-i));
			c.endUpdateChanged();
		}
	}
	@SuppressWarnings("unchecked")
	public void distribute() throws Exception {
		if (clusters == null)
			initialize();
		if (dictionary == null)
			throw new Exception("Dictionary is missing from Cluster.");
		if (documents.isEmpty())
			throw new Exception("System has no documents to cluster.");
		boolean converged = false;
		boolean minDis;
		int numberOfClusters = clusters.size();
		while(! converged) {
			Vector<Document> remaining_docs = (Vector<Document>) documents.clone();
			Vector<Document> remaining_docs_clone;
			converged = true;
			for (int i = 0; i < numberOfClusters-1; i++) {
				Cluster cluster = clusters.elementAt(i);
				cluster.startUpdate();
				remaining_docs_clone = (Vector<Document>) remaining_docs.clone();
				for (Document doc : remaining_docs_clone) {
					minDis = true;
					double dis = doc.getTFV().distanceTo(cluster.getCentroid());
					for (int j = i+1; j < clusters.size(); i++) {
						Cluster other_cluster = clusters.elementAt(j);
						double other_dis = doc.getTFV().distanceTo(other_cluster.getCentroid());
						if (other_dis < dis) {
							minDis = false;
							break;
						}
					}
					if (minDis) {
						cluster.addDocument(doc);
						remaining_docs.remove(doc);
					}
				}
				if (cluster.endUpdateChanged())
					converged = false;
			}
			Cluster cluster = clusters.elementAt(numberOfClusters-1);
			for (Document doc : remaining_docs)
				cluster.addDocument(doc);
		}
	}
	public void resize() {
		// TODO: resize according to cluster relative size;
	}
	public Vector<Document> documents() {
		return documents;
	}
	public Vector<Cluster> getClusters() {
		return clusters;
	}

}
